/*
Copyright (c) 2010 Tink Ltd - http://www.tink.ws

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package ws.tink.spark.layouts
{
	import flash.geom.Point;
	
	import mx.core.IUIComponent;
	import mx.core.ScrollPolicy;
	import mx.utils.MatrixUtil;
	
	import spark.components.Scroller;
	import spark.components.supportClasses.GroupBase;
	import spark.components.supportClasses.ScrollBarBase;
	import spark.components.supportClasses.Skin;
	import spark.core.IViewport;
	import spark.layouts.supportClasses.LayoutBase;
	import spark.layouts.supportClasses.LayoutElementHelper;
	
	[ExcludeClass]
	
	/**
	 *  @private
	 */
	public class InlineScrollerLayout extends LayoutBase
	{
		public function InlineScrollerLayout()    
		{
			super();
		}
		
		/**
		 *  @private
		 *  SDT - Scrollbar Display Threshold.  If the content size exceeds the
		 *  viewport's size by SDT, then we show a scrollbar.  For example, if the 
		 *  contentWidth >= viewport width + SDT, show the horizontal scrollbar.
		 */
		private static const SDT:Number = 1.0;
		
		/**
		 *  @private
		 *  Used by updateDisplayList() to prevent looping.
		 */
		private var invalidationCount:int = 0;
		
		
		/**
		 *  @private
		 */
		private function getScroller():Scroller
		{
			var g:Skin = target as Skin;
			return (g && ("hostComponent" in g)) ? Object(g).hostComponent as Scroller : null;
		}
		
		/**
		 *  @private
		 *  Returns the viewport's content size transformed into the Scroller's coordiante
		 *  system.   This makes it possible to compare the viewport size (also reported
		 *  relative to the Scroller) and the content size when a transform has been applied
		 *  to the viewport.  See http://bugs.adobe.com/jira/browse/SDK-19702
		 */
		private function getLayoutContentSize(viewport:IViewport):Point
		{
			// TODO(hmuller):prefer to do nothing if transform doesn't change size, see UIComponent/nonDeltaLayoutMatrix()
			var cw:Number = viewport.contentWidth;
			var ch:Number = viewport.contentHeight;
			if (((cw == 0) && (ch == 0)) || (isNaN(cw) || isNaN(ch)))
				return new Point(0,0);
			return MatrixUtil.transformSize(cw, ch, viewport.getLayoutMatrix());
		}
		
		//----------------------------------
		//  hsbVisible
		//----------------------------------    
		
		private var hsbScaleX:Number = 1;
		private var hsbScaleY:Number = 1;
		
		/**
		 *  @private
		 */
		private function get hsbVisible():Boolean
		{
			var hsb:ScrollBarBase = getScroller().horizontalScrollBar;
			return hsb && hsb.visible;
		}
		
		/**
		 *  @private 
		 *  To make the scrollbars invisible to methods like getRect() and getBounds() 
		 *  as well as to methods based on them like hitTestPoint(), we set their scale 
		 *  to 0.  More info about this here: http://bugs.adobe.com/jira/browse/SDK-21540
		 */
		private function set hsbVisible(value:Boolean):void
		{
			var hsb:ScrollBarBase = getScroller().horizontalScrollBar;
			if (!hsb)
				return;
			
			hsb.includeInLayout = hsb.visible = value;
			if (value)
			{
				if (hsb.scaleX == 0) 
					hsb.scaleX = hsbScaleX;
				if (hsb.scaleY == 0) 
					hsb.scaleY = hsbScaleY;
			}
			else 
			{
				if (hsb.scaleX != 0)
					hsbScaleX = hsb.scaleX;
				if (hsb.scaleY != 0)
					hsbScaleY = hsb.scaleY;
				hsb.scaleX = hsb.scaleY = 0;            
			}
		}
		
		/**
		 *  @private
		 *  Returns the vertical space required by the horizontal scrollbar.   
		 *  That's the larger of the minViewportInset and the hsb's preferred height.   
		 * 
		 *  Computing this value is complicated by the fact that if the HSB is currently 
		 *  hsbVisible=false, then it's scaleX,Y will be 0, and it's preferred size is 0.  
		 *  For that reason we specify postLayoutTransform=false to getPreferredBoundsHeight() 
		 *  and then multiply by the original scale factor, hsbScaleY.
		 */
//		private function hsbRequiredHeight():Number 
//		{
//			var scroller:Scroller = getScroller();
//			var minViewportInset:Number = scroller.minViewportInset;
//			var hsb:ScrollBarBase = scroller.horizontalScrollBar;
//			var sy:Number = (hsbVisible) ? 1 : hsbScaleY;
//			return Math.max(minViewportInset, hsb.getPreferredBoundsHeight(hsbVisible) * sy);
//		}
		private function hsbRequiredWidth():Number 
		{
			var scroller:Scroller = getScroller();
			var minViewportInset:Number = scroller.minViewportInset;
			var hsb:ScrollBarBase = scroller.horizontalScrollBar;
			var sx:Number = (hsbVisible) ? 1 : hsbScaleX;
			return Math.max(minViewportInset, ( hsb.getPreferredBoundsWidth(hsbVisible) * 2 ) * sx);
		}
		
		/**
		 *  @private
		 *  Return true if the specified dimensions provide enough space to layout 
		 *  the horizontal scrollbar (hsb) at its minimum size.   The HSB is assumed 
		 *  to be non-null and visible.
		 * 
		 *  If includeVSB is false we check to see if the HSB woudl fit if the 
		 *  VSB wasn't visible.
		 */
		private function hsbFits(w:Number, h:Number, includeVSB:Boolean=true):Boolean
		{
			if (vsbVisible && includeVSB)
			{
				var vsb:ScrollBarBase = getScroller().verticalScrollBar;            
//				w -= vsb.getPreferredBoundsWidth();
				h -= vsb.getMinBoundsHeight();
			}
			var hsb:ScrollBarBase = getScroller().horizontalScrollBar;        
			return (w >= hsb.getMinBoundsWidth()) && (h >= hsb.getPreferredBoundsHeight());
		}
		
		//----------------------------------
		//  vsbVisible
		//----------------------------------    
		
		private var vsbScaleX:Number = 1;
		private var vsbScaleY:Number = 1;
		
		/**
		 *  @private
		 */
		private function get vsbVisible():Boolean
		{
			var vsb:ScrollBarBase = getScroller().verticalScrollBar;
			return vsb && vsb.visible;
		}
		
		/**
		 *  @private
		 *  The logic here is the same as for the horizontal scrollbar, see above.
		 */
		private function set vsbVisible(value:Boolean):void
		{
			
			
			var vsb:ScrollBarBase = getScroller().verticalScrollBar;
			if (!vsb)
				return;
			
			vsb.includeInLayout = vsb.visible = value;
			if (value)
			{
				if (vsb.scaleX == 0) 
					vsb.scaleX = vsbScaleX;
				if (vsb.scaleY == 0) 
					vsb.scaleY = vsbScaleY;
			}
			else 
			{
				if (vsb.scaleX != 0)
					vsbScaleX = vsb.scaleX;
				if (vsb.scaleY != 0)
					vsbScaleY = vsb.scaleY;
				vsb.scaleX = vsb.scaleY = 0;            
			}
		}
		
		/**
		 *  @private
		 *  Returns the vertical space required by the horizontal scrollbar.   
		 *  That's the larger of the minViewportInset and the hsb's preferred height.  
		 *  
		 *  Computing this value is complicated by the fact that if the HSB is currently 
		 *  hsbVisible=false, then it's scaleX,Y will be 0, and it's preferred size is 0.  
		 *  For that reason we specify postLayoutTransform=false to getPreferredBoundsWidth() 
		 *  and then multiply by the original scale factor, vsbScaleX.
		 */
//		private function vsbRequiredWidth():Number 
//		{
//			var scroller:Scroller = getScroller();
//			var minViewportInset:Number = scroller.minViewportInset;
//			var vsb:ScrollBarBase = scroller.verticalScrollBar;
//			var sx:Number = (vsbVisible) ? 1 : vsbScaleX;
//			return Math.max(minViewportInset, vsb.getPreferredBoundsWidth(vsbVisible) * sx);
//		}
		private function vsbRequiredHeight():Number 
		{
			var scroller:Scroller = getScroller();
			var minViewportInset:Number = scroller.minViewportInset;
			var vsb:ScrollBarBase = scroller.verticalScrollBar;
			var sy:Number = (vsbVisible) ? 1 : vsbScaleY;
			
			return Math.max(minViewportInset, ( vsb.getPreferredBoundsHeight(vsbVisible) * 2 ) * sy);
		}
		
		/**
		 *  @private
		 *  Return true if the specified dimensions provide enough space to layout 
		 *  the vertical scrollbar (vsb) at its minimum size.   The VSB is assumed 
		 *  to be non-null and visible.
		 * 
		 *  If includeHSB is false, we check to see if the VSB would fit if the 
		 *  HSB wasn't visible.
		 */
		private function vsbFits(w:Number, h:Number, includeHSB:Boolean=true):Boolean
		{
			if (hsbVisible && includeHSB)
			{
				var hsb:ScrollBarBase = getScroller().horizontalScrollBar;            
				w -= hsb.getMinBoundsWidth();
//				h -= hsb.getPreferredBoundsHeight();
			}
			var vsb:ScrollBarBase = getScroller().verticalScrollBar;  
			return (w >= vsb.getPreferredBoundsWidth()) && (h >= vsb.getMinBoundsHeight());
		}
		
		/**
		 * @private
		 *  Computes the union of the preferred size of the visible scrollbars 
		 *  and the viewport if target.measuredSizeIncludesScrollbars=true, otherwise
		 *  it's just the preferred size of the viewport.
		 * 
		 *  This becomes the ScrollerSkin's measuredWidth,Height.
		 *    
		 *  The viewport does not contribute to the minimum size unless its
		 *  explicit size has been set.
		 */
		override public function measure():void
		{
			const scroller:Scroller = getScroller();
			if (!scroller) 
				return;
			
			const minViewportInset:Number = scroller.minViewportInset;
			const measuredSizeIncludesScrollBars:Boolean = scroller.measuredSizeIncludesScrollBars;
			
			var measuredW:Number = minViewportInset;
			var measuredH:Number = minViewportInset;
			
			const hsb:ScrollBarBase = scroller.horizontalScrollBar;
			var showHSB:Boolean = false;
			var hAuto:Boolean = false;
			if (measuredSizeIncludesScrollBars)
				switch(scroller.getStyle("horizontalScrollPolicy")) 
				{
					case ScrollPolicy.ON: 
						if (hsb) showHSB = true; 
						break;
					case ScrollPolicy.AUTO: 
						if (hsb) showHSB = hsb.visible;
						hAuto = true;
						break;
				} 
			
			const vsb:ScrollBarBase = scroller.verticalScrollBar;
			var showVSB:Boolean = false;
			var vAuto:Boolean = false;
			if (measuredSizeIncludesScrollBars)
				switch(scroller.getStyle("verticalScrollPolicy")) 
				{
					case ScrollPolicy.ON: 
						if (vsb) showVSB = true; 
						break;
					case ScrollPolicy.AUTO: 
						if (vsb) showVSB = vsb.visible;
						vAuto = true;
						break;
				}
			
			 measuredW += (showHSB) ? hsbRequiredWidth() : minViewportInset;
			 measuredH += (showVSB) ? vsbRequiredHeight() : minViewportInset;
			
			// The measured size of the viewport is just its preferredBounds, except:
			// don't give up space if doing so would make an auto scrollbar visible.
			// In other words, if an auto scrollbar isn't already showing, and using
			// the preferred size would force it to show, and the current size would not,
			// then use its current size as the measured size.  Note that a scrollbar
			// is only shown if the content size is greater than the viewport size 
			// by at least SDT.
			
			var viewport:IViewport = scroller.viewport;
			if (viewport)
			{
				if (measuredSizeIncludesScrollBars)
				{
					var contentSize:Point = getLayoutContentSize(viewport);
					
					var viewportPreferredW:Number =  viewport.getPreferredBoundsWidth();
					var viewportContentW:Number = contentSize.x;
					var viewportW:Number = viewport.getLayoutBoundsWidth();  // "current" size
					var currentSizeNoHSB:Boolean = !isNaN(viewportW) && ((viewportW + SDT) > viewportContentW);
					if (hAuto && !showHSB && ((viewportPreferredW + SDT) <= viewportContentW) && currentSizeNoHSB)
						measuredW += viewportW;
					else
						measuredW += Math.max(viewportPreferredW, (showHSB) ? hsb.getMinBoundsWidth() : 0);
					
					var viewportPreferredH:Number = viewport.getPreferredBoundsHeight();
					var viewportContentH:Number = contentSize.y;
					var viewportH:Number = viewport.getLayoutBoundsHeight();  // "current" size
					var currentSizeNoVSB:Boolean = !isNaN(viewportH) && ((viewportH + SDT) > viewportContentH);
					if (vAuto && !showVSB && ((viewportPreferredH + SDT) <= viewportContentH) && currentSizeNoVSB)
						measuredH += viewportH;
					else
						measuredH += Math.max(viewportPreferredH, (showVSB) ? vsb.getMinBoundsHeight() : 0);
				}
				else
				{
					measuredW += viewport.getPreferredBoundsWidth();
					measuredH += viewport.getPreferredBoundsHeight();
				}
			}
			
			var minW:Number = minViewportInset * 2;
			var minH:Number = minViewportInset * 2;
			
			// If the viewport's explicit size is set, then 
			// include that in the scroller's minimum size
			
			var viewportUIC:IUIComponent = viewport as IUIComponent;
			var explicitViewportW:Number = viewportUIC ? viewportUIC.explicitWidth : NaN;
			var explicitViewportH:Number = viewportUIC ? viewportUIC.explicitHeight : NaN;
			
			if (!isNaN(explicitViewportW)) 
				minW += explicitViewportW;
			
			if (!isNaN(explicitViewportH)) 
				minH += explicitViewportH;
			
			var g:GroupBase = target;
			g.measuredWidth = Math.ceil(measuredW);
			g.measuredHeight = Math.ceil(measuredH);
			g.measuredMinWidth = Math.ceil(minW); 
			g.measuredMinHeight = Math.ceil(minH);
		}
		
		/**
		 *  @return Returns the maximum value for an element's dimension so that the component doesn't
		 *  spill out of the container size. Calculations are based on the layout rules.
		 *  Pass in unscaledWidth, hCenter, left, right, childX to get a maxWidth value.
		 *  Pass in unscaledHeight, vCenter, top, bottom, childY to get a maxHeight value.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion Flex 4
		 */
		static private function maxSizeToFitIn(totalSize:Number,
											   center:Number,
											   lowConstraint:Number,
											   highConstraint:Number,
											   position:Number):Number
		{
			if (!isNaN(center))
			{
				// (1) x == (totalSize - childWidth) / 2 + hCenter
				// (2) x + childWidth <= totalSize
				// (3) x >= 0
				//
				// Substitue x in (2):
				// (totalSize - childWidth) / 2 + hCenter + childWidth <= totalSize
				// totalSize - childWidth + 2 * hCenter + 2 * childWidth <= 2 * totalSize
				// 2 * hCenter + childWidth <= totalSize se we get:
				// (3) childWidth <= totalSize - 2 * hCenter
				//
				// Substitute x in (3):
				// (4) childWidth <= totalSize + 2 * hCenter
				//
				// From (3) & (4) above we get:
				// childWidth <= totalSize - 2 * abs(hCenter)
				
				return totalSize - 2 * Math.abs(center);
			}
			else if (!isNaN(lowConstraint))
			{
				// childWidth + left <= totalSize
				return totalSize - lowConstraint;
			}
			else if (!isNaN(highConstraint))
			{
				// childWidth + right <= totalSize
				return totalSize - highConstraint;
			}
			else
			{
				// childWidth + childX <= totalSize
				return totalSize - position;
			}
		}
		
		/** 
		 *  @private
		 *  Arrange the viewport and scrollbars conventionally within
		 *  the specified width and height: vertical scrollbar on the 
		 *  right, horizontal scrollbar along the bottom.
		 * 
		 *  Scrollbars for which the corresponding scrollPolicy=auto 
		 *  are made visible if the viewport's content size is bigger 
		 *  than the actual size.   This introduces the possibility of
		 *  validateSize,DisplayList() looping because the measure() 
		 *  method computes the size of the viewport and the currently
		 *  visible scrollbars. 
		 * 
		 */
		override public function updateDisplayList(w:Number, h:Number):void
		{  
			
			var scroller:Scroller = getScroller();
			if (!scroller) 
				return;
			
			var viewport:IViewport = scroller.viewport;
			var hsb:ScrollBarBase = scroller.horizontalScrollBar;
			var vsb:ScrollBarBase = scroller.verticalScrollBar;
			var minViewportInset:Number = scroller.minViewportInset;
			
			var contentW:Number = 0;
			var contentH:Number = 0;
			if (viewport)
			{
				var contentSize:Point = getLayoutContentSize(viewport);
				contentW = contentSize.x;
				contentH = contentSize.y;
			}
			
			// If the viewport's size has been explicitly set (not typical) then use it
			// The initial values for viewportW,H are only used to decide if auto scrollbars
			// should be shown. 
			
			var viewportUIC:IUIComponent = viewport as IUIComponent;
			var explicitViewportW:Number = viewportUIC ? viewportUIC.explicitWidth : NaN;
			var explicitViewportH:Number = viewportUIC ? viewportUIC.explicitHeight : NaN;
			
			var viewportW:Number = isNaN(explicitViewportW) ? (w - (minViewportInset * 2)) : explicitViewportW;
			var viewportH:Number = isNaN(explicitViewportH) ? (h - (minViewportInset * 2)) : explicitViewportH;
			
			// Decide which scrollbars will be visible based on the viewport's content size
			// and the scroller's scroll policies.  A scrollbar is shown if the content size 
			// greater than the viewport's size by at least SDT.
			
			var oldShowHSB:Boolean = hsbVisible;
			var oldShowVSB:Boolean = vsbVisible;
			
			var hAuto:Boolean = false; 
			switch(scroller.getStyle("horizontalScrollPolicy")) 
			{
				case ScrollPolicy.ON: 
					hsbVisible = true;
					break;
				
				case ScrollPolicy.AUTO: 
					if (hsb && viewport)
					{
						hAuto = true;
						hsbVisible = (contentW >= (viewportW + SDT));
					} 
					break;
				
				default:
					hsbVisible = false;
			} 
			
			var vAuto:Boolean = false;
			switch(scroller.getStyle("verticalScrollPolicy")) 
			{
				case ScrollPolicy.ON: 
					vsbVisible = true; 
					break;
				
				case ScrollPolicy.AUTO: 
					if (vsb && viewport)
					{ 
						vAuto = true;
						vsbVisible = (contentH >= (viewportH + SDT));
					}                        
					break;
				
				default:
					vsbVisible = false;
			}
			
			// Reset the viewport's width,height to account for the visible scrollbars, unless
			// the viewport's size was explicitly set, then we just use that. 
			
			if (isNaN(explicitViewportW))
				viewportW = w - ((hsbVisible) ? (minViewportInset + hsbRequiredWidth()) : (minViewportInset * 2));
			else 
				viewportW = explicitViewportW;
			
			if (isNaN(explicitViewportH))
				viewportH = h - ((vsbVisible) ? (minViewportInset + vsbRequiredHeight()) : (minViewportInset * 2));
			else 
				viewportH = explicitViewportH;
			
			// If the scrollBarPolicy is auto, and we're only showing one scrollbar, 
			// the viewport may have shrunk enough to require showing the other one.
			
			var hsbIsDependent:Boolean = false;
			var vsbIsDependent:Boolean = false;
			
			if (vsbVisible && !hsbVisible && hAuto && (contentW >= (viewportW + SDT)))
				hsbVisible = hsbIsDependent = true;
			else if (!vsbVisible && hsbVisible && vAuto && (contentH >= (viewportH + SDT)))
				vsbVisible = vsbIsDependent = true;
			
			
			// If the HSB doesn't fit, hide it and give the space back.   Likewise for VSB.
			// If both scrollbars are supposed to be visible but they don't both fit, 
			// then prefer to show the "non-dependent" auto scrollbar if we added the second
			// "dependent" auto scrollbar because of the space consumed by the first.
			
			if (hsbVisible && vsbVisible) 
			{
				if (hsbFits(w, h) && vsbFits(w, h))
				{
					// Both scrollbars fit, we're done.
				}
				else if (!hsbFits(w, h, false) && !vsbFits(w, h, false))
				{
					// Neither scrollbar would fit, even if the other scrollbar wasn't visible.
					hsbVisible = false;
					vsbVisible = false;
				}
				else
				{
					// Only one of the scrollbars will fit.  If we're showing a second "dependent"
					// auto scrollbar because the first scrollbar consumed enough space to
					// require it, if the first scrollbar doesn't fit, don't show either of them.
					
					if (hsbIsDependent)
					{
						if (vsbFits(w, h, false))  // VSB will fit if HSB isn't shown   
							hsbVisible = false;
						else 
							vsbVisible = hsbVisible = false;
						
					}
					else if (vsbIsDependent)
					{
						if (hsbFits(w, h, false)) // HSB will fit if VSB isn't shown
							vsbVisible = false;
						else
							hsbVisible = vsbVisible = false; 
					}
					else if (vsbFits(w, h, false)) // VSB will fit if HSB isn't shown
						hsbVisible = false;
					else // hsbFits(w, h, false)   // HSB will fit if VSB isn't shown
						vsbVisible = false;
				}
			}
			else if (hsbVisible && !hsbFits(w, h))  // just trying to show HSB, but it doesn't fit
				hsbVisible = false;
			else if (vsbVisible && !vsbFits(w, h))  // just trying to show VSB, but it doesn't fit
				vsbVisible = false;
			
			// Reset the viewport's width,height to account for the visible scrollbars, unless
			// the viewport's size was explicitly set, then we just use that.
			
			if (isNaN(explicitViewportW))
				viewportW = w - ((hsbVisible) ? (minViewportInset + hsbRequiredWidth()) : (minViewportInset * 2));
			else 
				viewportW = explicitViewportW;
			
			if (isNaN(explicitViewportH))
				viewportH = h - ((vsbVisible) ? (minViewportInset + vsbRequiredHeight()) : (minViewportInset * 2));
			else 
				viewportH = explicitViewportH;
			
			// Layout the viewport and scrollbars.
			
			if (viewport)
			{
				viewport.setLayoutBoundsSize(viewportW, viewportH);
				viewport.setLayoutBoundsPosition( minViewportInset + ( ( w - viewportW ) / 2 ), minViewportInset + ( ( h - viewportH ) / 2 ) );
			}
			
			var center:Number;
			var bounds:Number;
			var startPosition:Number;
			var endPosition:Number;
			var size:Number;
			var position:Number;
			
			if (hsbVisible)
			{
				center = LayoutElementHelper.parseConstraintValue( hsb.verticalCenter );
				bounds = hsb.getLayoutBoundsY();
				startPosition = LayoutElementHelper.parseConstraintValue(hsb.top);
				endPosition = LayoutElementHelper.parseConstraintValue(hsb.bottom);
				size = getSize( center,
					bounds,
					startPosition,
					endPosition,
					hsb.percentHeight, viewportH,
					hsb.getMinBoundsHeight(), hsb.getMaxBoundsHeight() )
				
				hsb.setLayoutBoundsSize( Math.max( hsb.getMinBoundsWidth(), w ), size );
				
				position = getPosition( center, bounds,
					startPosition, endPosition,
					hsb.getLayoutBoundsHeight(), viewportH,
					h, minViewportInset );
				
				hsb.setLayoutBoundsPosition( 0, position );
////				var hsbW:Number = (vsbVisible) ? w - vsb.getPreferredBoundsWidth() : w;
//				var hsbH:Number = hsb.getPreferredBoundsHeight();
//				hsb.setLayoutBoundsSize(Math.max(hsb.getMinBoundsWidth(), w), hsbH);
//				hsb.setLayoutBoundsPosition(0, ( h - hsbH ) / 2);
			}
			
			if (vsbVisible)
			{
				center = LayoutElementHelper.parseConstraintValue( vsb.horizontalCenter );
				bounds = vsb.getLayoutBoundsX();
				startPosition = LayoutElementHelper.parseConstraintValue(vsb.left);
				endPosition = LayoutElementHelper.parseConstraintValue(vsb.right);
				
				size = getSize( center,
					bounds,
					startPosition,
					endPosition,
					vsb.percentWidth, viewportW,
					vsb.getMinBoundsWidth(), vsb.getMaxBoundsWidth() );
					
				vsb.setLayoutBoundsSize( size, Math.max( vsb.getMinBoundsHeight(), h ) );
				
				position = getPosition( center, bounds,
					startPosition, endPosition,
					vsb.getLayoutBoundsWidth(), viewportW,
					w, minViewportInset );
				
				vsb.setLayoutBoundsPosition( position, 0 );
			}
			
			// If we've added an auto scrollbar, then the measured size is likely to have been wrong.
			// There's a risk of looping here, so we count.  
			if ((invalidationCount < 2) && (((vsbVisible != oldShowVSB) && vAuto) || ((hsbVisible != oldShowHSB) && hAuto)))
			{
				target.invalidateSize();
				
				// If the viewport's layout is virtual, it's possible that its
				// measured size changed as a consequence of laying it out,
				// so we invalidate its size as well.
				var viewportGroup:GroupBase = viewport as GroupBase;
				if (viewportGroup && viewportGroup.layout && viewportGroup.layout.useVirtualLayout)
					viewportGroup.invalidateSize();
				
				invalidationCount += 1; 
			}
			else
				invalidationCount = 0;
			
			target.setContentSize(w, h);
			
			
			
		}
		
		
		protected function getSize( center:Number, position:Number,
									startPositon:Number, endPosition:Number,
									percent:Number, viewportSize:Number,
									minBounds:Number, maxBounds:Number ):Number
		{
			var elementMax:Number = NaN; 
			
			// Calculate size
			var childSize:Number = NaN;
			
			if (!isNaN(percent))
			{
				var available:Number = viewportSize;
				if (!isNaN(startPositon))
					available -= startPositon;
				if (!isNaN(endPosition))
					available -= endPosition;
				
				childSize = Math.round(available * Math.min(percent * 0.01, 1));
				elementMax = Math.min(maxBounds,
					maxSizeToFitIn(viewportSize, center, startPositon, endPosition, position));
			}
			else if (!isNaN(startPositon) && !isNaN(endPosition))
			{
				childSize = viewportSize - endPosition - startPositon;
			}
			
			// Apply min and max constraints, make sure min is applied last. In the cases
			// where childSize and childHeight are NaN, setLayoutBoundsSize will use preferredSize
			// which is already constrained between min and max.
			if (!isNaN(childSize))
			{
				if (isNaN(elementMax))
					elementMax = maxBounds;
				childSize = Math.max(minBounds, Math.min(elementMax, childSize));
			}
			
			return childSize;
		}
		
		protected function getPosition( center:Number, position:Number,
											   startPositon:Number, endPosition:Number,
											   layoutBounds:Number, viewportSize:Number,
												unscaledSize:Number,
												minInset:Number ):Number
		{
			var childX:Number = NaN;
			
			// Horizontal position
			if (!isNaN(center))
				childX = Math.round( ( (viewportSize - layoutBounds) / 2 + center ) );
			else if (!isNaN(startPositon))
				childX = startPositon;
			else if (!isNaN(endPosition))
				childX = viewportSize - layoutBounds - endPosition;
			else
				childX = position;
			
			childX += minInset + ( ( unscaledSize - viewportSize ) / 2 );
			
			return childX;
		}
		
			
	}
	
	
}
